package com.mushuilingfeng.ddd.user.infrastructure.repository;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.mushuilingfeng.ddd.user.domain.base.DataPageResult;
import com.mushuilingfeng.ddd.user.domain.base.GenderEnum;
import com.mushuilingfeng.ddd.user.domain.user.dto.UserQueryDto;
import com.mushuilingfeng.ddd.user.domain.user.entity.User;
import com.mushuilingfeng.ddd.user.domain.user.enums.UserStatusEnum;
import com.mushuilingfeng.ddd.user.domain.user.repository.IUserRepository;
import com.mushuilingfeng.ddd.user.domain.user.vo.UserPersonInfo;
import com.mushuilingfeng.ddd.user.infrastructure.cover.IPODataCover;
import com.mushuilingfeng.ddd.user.infrastructure.repository.dao.mapperservice.UserMapperService;
import com.mushuilingfeng.ddd.user.infrastructure.repository.dao.mapperservice.UserPersonInfoMapperService;
import com.mushuilingfeng.ddd.user.infrastructure.repository.po.UserPO;
import com.mushuilingfeng.ddd.user.infrastructure.repository.po.UserPersonInfoPO;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author
 * @date 2024/9/30
 */
@Repository
@AllArgsConstructor
public class UserRepositoryImpl extends BaseMybatisRepository<User> implements IUserRepository {
    private final UserMapperService userMapperService;
    private final UserPersonInfoMapperService userPersonInfoMapperService;
    
    private User userPO2User(UserPO po) {
        if (null == po) {
            return null;
        }
        User user = IPODataCover.INSTANCE.userPO2User(po);
        user.setGender(GenderEnum.get(po.getGender()));
        user.setStatus(UserStatusEnum.get(po.getStatus()));
        return user;
    }
    
    @Override
    public User findByLoginAccount(String loginAccount) {
        List<UserPO> poList = userMapperService.lambdaQuery().eq(UserPO::getLoginAccount, loginAccount).list();
        if (CollectionUtil.isEmpty(poList)) {
            return null;
        }
        if (poList.size() > 1) {
            throw new IllegalArgumentException("findByLoginAccount by [" + loginAccount + "] return more than one result");
        }
        return userPO2User(poList.get(0));
    }
    
    @Override
    public User findByTelephone(String telephone) {
        List<UserPO> poList = userMapperService.lambdaQuery().eq(UserPO::getTelephone, telephone).list();
        if (CollectionUtil.isEmpty(poList)) {
            return null;
        }
        if (poList.size() > 1) {
            throw new IllegalArgumentException("findByTelephone by [" + telephone + "] return more than one result");
        }
        return userPO2User(poList.get(0));
    }
    
    @Override
    public void updatePassword(Long userId, String newPassword) {
        userMapperService.lambdaUpdate().eq(UserPO::getId, userId).set(UserPO::getLoginPassword, newPassword).update();
    }
    
    @Override
    public void incrementLoginErrorTimes(String userAccount) {
        userMapperService.getBaseMapper().incrementLoginErrorTimes(userAccount);
    }
    
    @Override
    public void clearLoginErrorTimes(String userAccount) {
        userMapperService.lambdaUpdate().eq(UserPO::getLoginAccount, userAccount).set(UserPO::getLoginErrorTimes, 0).update();
    }
    
    @Override
    public int getLoginErrorTimes(String userAccount) {
        List<UserPO> userPOList = userMapperService.lambdaQuery().eq(UserPO::getLoginAccount, userAccount).list();
        if (!CollectionUtil.isEmpty(userPOList)) {
            return userPOList.get(0).getLoginErrorTimes();
        }
        return 0;
    }
    
    @Override
    public DataPageResult<User> list(UserQueryDto dto) {
        LambdaQueryChainWrapper<UserPO> lambdaQuery = userMapperService.lambdaQuery();
        if (!StrUtil.isBlankIfStr(dto.getAccount())) {
            lambdaQuery.eq(UserPO::getLoginAccount, dto.getAccount());
        }
        if (!StrUtil.isBlankIfStr(dto.getTel())) {
            lambdaQuery.eq(UserPO::getTelephone, dto.getTel());
        }
        
        Page<UserPO> page = new Page<>(dto.getPageNum(), dto.getPageSize());
        lambdaQuery.page(page);
        List<UserPO> userPOList = lambdaQuery.list();
        
        DataPageResult<User> pageResult = new DataPageResult<>();
        pageResult.setPageNum(dto.getPageNum());
        pageResult.setPageSize(dto.getPageSize());
        pageResult.setTotal(page.getSize());
        List<User> userList = new LinkedList<>();
        if (!CollectionUtil.isEmpty(userPOList)) {
            userPOList.forEach(userPO -> userList.add(userPO2User(userPO)));
        }
        // 设置其他字段
        if (!userList.isEmpty()) {
            List<UserPersonInfoPO> userPersonInfoPOList = userPersonInfoMapperService.lambdaQuery().in(UserPersonInfoPO::getUserId,
                    userList.stream().map(User::getId).collect(Collectors.toList())).list();
            if (!CollectionUtil.isEmpty(userPersonInfoPOList)) {
                Map<Long, UserPersonInfoPO> userPersonInfoPOMap = userPersonInfoPOList.stream().collect(Collectors.toMap(UserPersonInfoPO::getUserId,
                        t -> t));
                userList.forEach(user -> user.setPersonInfo(IPODataCover.INSTANCE.userPersonInfoPO2UserPersonInfo(userPersonInfoPOMap.get(user.getId()))));
            }
        }
        pageResult.setData(userList);
        return pageResult;
    }
    
    @Override
    public void updateNickName(Long userId, String nickName) {
        userMapperService.lambdaUpdate().set(UserPO::getNickName, nickName).eq(UserPO::getId, userId).update();
    }
    
    @Override
    public void updateTelephone(Long userId, String telephone) {
        userMapperService.lambdaUpdate().set(UserPO::getTelephone, telephone).eq(UserPO::getId, userId).update();
    }
    
    @Transactional
    @Override
    public void updatePersonInfo(Long userId, UserPersonInfo personInfo) {
        List<UserPersonInfoPO> userPersonInfoPOList = userPersonInfoMapperService.lambdaQuery().eq(UserPersonInfoPO::getUserId, userId).list();
        if (CollectionUtil.isEmpty(userPersonInfoPOList)) {
            // 先删除
            userPersonInfoMapperService.lambdaUpdate().eq(UserPersonInfoPO::getUserId, userId).remove();
        }
        // 再新增
        UserPersonInfoPO userPersonInfoPO = IPODataCover.INSTANCE.userPersonInfo2UserPersonInfoPO(personInfo);
        userPersonInfoPO.setUserId(userId);
        if (CollectionUtil.isEmpty(userPersonInfoPOList)) {
            userPersonInfoPO.setCreateTime(new Date());
        } else {
            userPersonInfoPO.setCreateTime(userPersonInfoPOList.get(0).getCreateTime());
        }
        userPersonInfoPO.setUpdateTime(new Date());
        userPersonInfoMapperService.save(userPersonInfoPO);
    }
    
    @Override
    public void updateStatus(Long userId, UserStatusEnum status) {
        userMapperService.lambdaUpdate().set(UserPO::getStatus, status.getCode()).eq(UserPO::getId, userId).update();
    }
    
    @Override
    public User get(Serializable id) {
        User user = userPO2User(userMapperService.getById(id));
        List<UserPersonInfoPO> personInfoPOList = userPersonInfoMapperService.lambdaQuery().eq(UserPersonInfoPO::getUserId, user.getId()).list();
        if (!CollectionUtil.isEmpty(personInfoPOList)) {
            user.setPersonInfo(IPODataCover.INSTANCE.userPersonInfoPO2UserPersonInfo(personInfoPOList.get(0)));
        }
        return user;
    }
    
    @Transactional
    @Override
    public void save(User entityOrObjectValue) {
        // 保存用户主表
        userMapperService.save(IPODataCover.INSTANCE.user2UserPO(entityOrObjectValue));
        // 保存用户个人信息表
        if (null != entityOrObjectValue.getPersonInfo()) {
            UserPersonInfoPO userPersonInfoPO = IPODataCover.INSTANCE.userPersonInfo2UserPersonInfoPO(entityOrObjectValue.getPersonInfo());
            userPersonInfoPO.setUserId(entityOrObjectValue.getId());
            userPersonInfoPO.setCreateTime(new Date());
            userPersonInfoPO.setUpdateTime(userPersonInfoPO.getCreateTime());
            userPersonInfoMapperService.save(userPersonInfoPO);
        }
    }
    
    @Override
    public void saveOrUpdate(User entityOrObjectValue) {
        throw new UnsupportedOperationException("not support saveOrUpdate method");
    }
    
    @Transactional
    @Override
    public void delete(Serializable id) {
        userMapperService.removeById(id);
        userPersonInfoMapperService.remove(new LambdaQueryWrapper<UserPersonInfoPO>().eq(UserPersonInfoPO::getUserId, id));
    }
}
